/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package percussiongenerator.gui.view;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.ResourceBundle;
import javax.swing.JPopupMenu;
import org.jfonia.connect5.basics.BasicValueNode;
import org.jfonia.connect5.basics.MutableValueNode;
import org.jfonia.connect5.intervals.Interval;
import org.jfonia.connect5.intervals.ListObserver;
import org.jfonia.connect5.relations.Relation;
import org.jfonia.constants.ModelConstants;
import org.jfonia.model.PerformanceNode;
import org.jfonia.model.PerformanceSequence;
import percussiongenerator.model.Track;

/**
 *
 * @author Jannes Plyson
 */
public class PianorollView extends View implements KeyListener{
    private MutableValueNode<Integer> minPitch;
    private MutableValueNode<Integer> maxPitch;
    private int lineHeight = 10;    
    public PianorollView(Track track,MutableValueNode<View> selectedView,MutableValueNode<ArrayList<Integer>> selectedInterval){
        super(track,selectedView,selectedInterval);
        viewOptions = new PianorollOptionsPanel();
        minPitch = new BasicValueNode<Integer>(0);
        maxPitch = new BasicValueNode<Integer>(0);
        minPitch.addObserver(this);
        maxPitch.addObserver(this);
        new MinMaxPitchRelation(track.getStaff().getToneSequence().getPerformanceSequence(), minPitch, maxPitch);
        setWidthNode();
    }

    @Override
    public void paintView(Graphics g) {   
        int lines = maxPitch.getValue()-minPitch.getValue()+1;
        int minP = minPitch.getValue();        
        g.setColor(Color.BLACK);
        PerformanceSequence seq = track.getStaff().getToneSequence().getPerformanceSequence();
        long length = seq.getDifference();
        int timeOffset = (int)(track.getTime()*getWidth()/length);
        g.fillRect(getWidth()-timeOffset-1, 0, 2, getHeight());
        for(int i = 0; i < seq.size(); i++){
            if(selectedInterval.getValue() != null && selectedInterval.getValue().contains(i)){
                g.setColor(selectedForegroundColor);
            }else{
                g.setColor(Color.BLACK);
            }
            PerformanceNode perNode = (PerformanceNode)seq.getIntervalFromIndex(i);
            if(perNode.getVelocity() > 0 && perNode.getPitch() != null){
                int yOffset = (perNode.getPitch()-minP)*lineHeight;
                int xLeft = (int)(perNode.getBegin() * getWidth() / length)-timeOffset;
                int width = (int)(perNode.getDifference() * getWidth() / length);
                g.fillRect(xLeft, yOffset, width, lineHeight);                
                if(xLeft < 0){
                    if(xLeft + width > 0){//leftOver from line at the beginning;
                        g.fillRect(0, yOffset, xLeft+width, lineHeight);//at the beginning
                        g.fillRect(getWidth()+xLeft, yOffset, -xLeft,lineHeight);//at the end
                    }else{
                        g.fillRect(getWidth()+xLeft, yOffset, width, lineHeight);
                    }
                }else{
                    g.fillRect(xLeft, yOffset, width, lineHeight);
                }
            }
        }        
        //draw lines and numbers;
        g.setColor(Color.GRAY);
        for(int i = 0; i <= lines; i++){
            g.drawLine(0, i*lineHeight, getWidth(), i*lineHeight);
            g.drawString("" + (i+minP),0,i*lineHeight);
        }
    }
    
    public void updateLanguage(ResourceBundle labels) {        
    }

    public void addElements(JPopupMenu popup, Object caller) {        
    }

    @Override
    public void onNotify(Object source){
        super.onNotify(source);
        if(source == minPitch || source == maxPitch){
            heightNode.setValue((maxPitch.getValue()-minPitch.getValue()+1)*lineHeight+1);            
        }
    }

    private void setWidthNode(){
        int w = (int)(track.getPerformanceLength()*zoom.getValue()/(ModelConstants.PERFORMANCE_TO_SECOND*100));
        widthNode.setValue(w);
    }

    public void intervalAdded(Interval interval) {
        setWidthNode();
    }

    public void intervalRemoved(Interval interval, int index) {
        setWidthNode();
    }

    public void intervalEndChanged(Interval interval) {
        setWidthNode();
    }

    @Override
    public void mousePressedExtra(MouseEvent e){
        long trackLength = track.getPerformanceLength();
        long timeOffset = track.getTime()*getWidth()/trackLength;
        long calculatedTime = (e.getX()+timeOffset)*trackLength/getWidth();
        PerformanceSequence perSeq = track.getStaff().getToneSequence().getPerformanceSequence();
        int yOffset = (((PerformanceNode)(perSeq.getIntervalFromValue(calculatedTime))).getPitch()-minPitch.getValue())*lineHeight;
        if(e.getY() >= yOffset && e.getY() <= yOffset+lineHeight){
            ArrayList<Integer> list = null;            
            if(ctrlPressed && selectedInterval.getValue() != null){
                list = selectedInterval.getValue();
            }else{
                list = new ArrayList<Integer>();
            }
            list.add(perSeq.indexOf(perSeq.getIntervalFromValue(calculatedTime)));
            selectedInterval.setValue(list);
            selectedInterval.notifyObservers();
        }else{
            selectedInterval.setValue(null);
        }
    }

    @Override
    public void zoomUpdated() {
        setWidthNode();
    }

    private class MinMaxPitchRelation implements Relation,ListObserver{
        private PerformanceSequence seq;
        private MutableValueNode<Integer> minNode;
        private MutableValueNode<Integer> maxNode;

        public MinMaxPitchRelation(PerformanceSequence seq,MutableValueNode<Integer> minNode,MutableValueNode<Integer> maxNode){
            this.seq = seq;
            this.minNode = minNode;
            this.maxNode = maxNode;
            switchOn();
        }

        public void updateMinMax(){
            int min = 192;
            int max = 0;
            for(int i = 0; i < seq.size(); i++){
                Integer value = ((PerformanceNode)seq.getIntervalFromIndex(i)).getPitch();
                if(value != null && value > 0){
                    min = Math.min(min,value);
                    max = Math.max(max, value);
                }            
            }
            minNode.setValue(min);
            maxNode.setValue(max);
        }

        public void switchOn(){
            seq.addObserver(this);
            for(int i = 0; i < seq.size(); i++){
                ((PerformanceNode)seq.getIntervalFromIndex(i)).getPitchNode().addObserver(this);
            }
            updateMinMax();
        }

        public void switchOff(){
            seq.removeObserver(this);
            for(int i = 0; i < seq.size(); i++){
                ((PerformanceNode)seq.getIntervalFromIndex(i)).getPitchNode().removeObserver(this);
            }
        }

        public void onNotify(Object source) {
            updateMinMax();
        }

        public void intervalAdded(Interval interval) {
            ((PerformanceNode)interval).getPitchNode().addObserver(this);
            updateMinMax();
        }

        public void intervalRemoved(Interval interval, int index) {
            ((PerformanceNode)interval).getPitchNode().removeObserver(this);
            updateMinMax();
        }

        public void intervalEndChanged(Interval interval) {

        }
        
    }

}
